package util

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"io"
	"ubt-rbac-service/internal/app/model"
	"ubt-rbac-service/internal/core/logger"
	"math/rand"
	"mime/multipart"
	"path"
	"strings"
	"time"
)

// UploadOss 上传文件到阿里云的OSS
func UploadOss(header *multipart.FileHeader, kind uint8) *model.ResJson {
	var (
		endPoint        = "oss-cn-huhehaote.aliyuncs.com"
		bucketUrl       = "https://ubt-oss.oss-cn-huhehaote.aliyuncs.com/"
		accessKeyId     = "LTAI5tDf1LVo5riuFefCbM9h"
		accessKeySecret = "Q2lyFbkYF0h7T1fAWmh8VEAOkz57Es"
		bucketName      = "ubt-oss"
	)
	// 创建OSSClient实例。
	client, err := oss.New(endPoint, accessKeyId, accessKeySecret)
	if err != nil {
		logger.Error("OSSClient实例创建失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，OSSClient实例创建失败，请联系管理员！"}
	}
	// 填写存储空间名称。
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		logger.Error("存储空间实例获取失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，存储空间实例获取失败，请联系管理员！"}
	}
	var fileName, extName, dateStr, filePath, srcPath, thumbPath, vstUrl, thumbUrl string
	dateStr = time.Now().Format("200601")
	fileName = header.Filename
	extName = path.Ext(fileName)
	extName = strings.ToLower(extName)
	fileName = fileName[:len(fileName)-len(extName)]
	var maxSize int64 = 200 * 1024 * 1024
	if !strings.Contains(".jpg.png.gif.bmp.jpeg.txt.xls.xlsx.doc.docx.pdf.zip.7z.rar.mp3.wma.mp4.mpeg", extName) {
		return &model.ResJson{Code: 403, Msg: "不允许上传的类型"}
	}
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		maxSize = 5 * 1024 * 1024
		// 目录需以正斜线结尾
		filePath = "img/src/" + dateStr + "/"
		srcPath = "img/" + dateStr + "/"
		thumbPath = "img/thumb/" + dateStr + "/"
		vstUrl = bucketUrl + "img/" + dateStr + "/"
		thumbUrl = bucketUrl + "img/thumb/" + dateStr + "/"

		// 创建源文件的目录
		if err = bucket.PutObject(srcPath, bytes.NewReader([]byte(""))); err != nil {
			logger.Error("源文件的目录创建失败。" + err.Error())
			return &model.ResJson{Code: 500, Msg: "上传目录创建失败"}
		}
		// 创建缩略图的目录
		if err = bucket.PutObject(thumbPath, bytes.NewReader([]byte(""))); err != nil {
			logger.Error("缩略图的目录创建失败。" + err.Error())
			return &model.ResJson{Code: 500, Msg: "上传目录创建失败"}
		}
	} else if strings.Contains(".txt.xls.xlsx.doc.docx.pdf", extName) {
		filePath = "doc/" + dateStr + "/"
		vstUrl = bucketUrl + "doc/" + dateStr + "/"
	} else if strings.Contains(".zip.7z.rar", extName) {
		filePath = "zip/" + dateStr + "/"
		vstUrl = bucketUrl + "zip/" + dateStr + "/"
	} else if strings.Contains(".mp3.wma.mp4.mpeg", extName) {
		maxSize = 500 * 1024 * 1024
		filePath = "media/" + dateStr + "/"
		vstUrl = bucketUrl + "media/" + dateStr + "/"
	} else {
		filePath = "others/" + dateStr + "/"
		vstUrl = bucketUrl + "others/" + dateStr + "/"
	}
	if maxSize < header.Size {
		return &model.ResJson{Code: 400, Msg: "对不起，您上传的文件超出了大小限制，请重试！"}
	}
	// 创建上传目录
	if err = bucket.PutObject(filePath, bytes.NewReader([]byte(""))); err != nil {
		logger.Error("上传目录创建失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "上传目录创建失败"}
	}
	//重命名文件
	newName := time.Now().Format("02150405") + fmt.Sprintf("%08v", rand.Intn(100000000)) + extName
	// 获取文件流
	file, err := header.Open()
	if err != nil {
		logger.Error("获取文件失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "获取文件失败"}
	}
	// 最后关闭文件
	defer func(file multipart.File) {
		if err = file.Close(); err != nil {
			logger.Error("关闭文件失败。" + err.Error())
		}
	}(file)
	// 文件转换成文件数组
	fileBytes := make([]byte, header.Size)
	if _, err = file.Read(fileBytes); err != nil {
		logger.Error("读取文件失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "读取文件失败"}
	}
	if err = bucket.PutObject(filePath+newName, bytes.NewReader(fileBytes)); err != nil {
		logger.Error("上传文件失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "上传文件失败"}
	}
	// 如果是图片格式的文件，则进行压缩和生成缩略图
	if strings.Contains(".jpg.png.gif.bmp.jpeg", extName) {
		// 压缩图片，高度设为0
		filePath += newName
		srcPath += newName
		style := "image/resize,m_mfit,w_1080,h_1080"
		process := fmt.Sprintf("%s|sys/saveas,o_%v,b_%v", style, base64.URLEncoding.EncodeToString([]byte(srcPath)), base64.URLEncoding.EncodeToString([]byte(bucketName)))
		if _, err = bucket.ProcessObject(filePath, process); err != nil {
			logger.Error("图片压缩失败。" + err.Error())
			return &model.ResJson{Code: 500, Msg: "图片压缩失败"}
		}
		// 生成缩略图
		thumbPath += newName
		style = "image/resize,m_lfit,w_240,h_240"
		process = fmt.Sprintf("%s|sys/saveas,o_%v,b_%v", style, base64.URLEncoding.EncodeToString([]byte(thumbPath)), base64.URLEncoding.EncodeToString([]byte(bucketName)))
		if _, err = bucket.ProcessObject(filePath, process); err != nil {
			logger.Error("缩略图生成失败。" + err.Error())
			return &model.ResJson{Code: 500, Msg: "缩略图生成失败"}
		}
	}
	fileId := NewNanoid()
	fileInfo := make(map[string]string)
	fileInfo["id"] = fileId
	fileInfo["name"] = fileName
	fileInfo["url"] = vstUrl + newName
	fileInfo["thumb"] = thumbUrl + newName
	return &model.ResJson{Code: 200, Msg: "ok", Data: fileInfo}
}

// DownloadOss 从阿里云的OSS下载
func DownloadOss(filePath string) *model.ResJson {
	var (
		endPoint        = "oss-cn-huhehaote.aliyuncs.com"
		accessKeyId     = "LTAI5tDf1LVo5riuFefCbM9h"
		accessKeySecret = "Q2lyFbkYF0h7T1fAWmh8VEAOkz57Es"
		bucketName      = "ubt-oss"
	)
	client, err := oss.New(endPoint, accessKeyId, accessKeySecret)
	if err != nil {
		logger.Error("OSSClient实例创建失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，OSSClient实例创建失败，请联系管理员！"}
	}
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		logger.Error("存储空间实例获取失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，存储空间实例获取失败，请联系管理员！"}
	}
	body, err := bucket.GetObject(filePath)
	if err != nil {
		logger.Error("所下载的文件不存在。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，所下载的文件不存在，请重试！"}
	}
	defer func(body io.ReadCloser) {
		err = body.Close()
		if err != nil {
			logger.Error("文件关闭失败。" + err.Error())
		}
	}(body)
	buf := new(bytes.Buffer)
	_, err = io.Copy(buf, body)
	if err != nil {
		logger.Error("文件下载失败。" + err.Error())
		return &model.ResJson{Code: 500, Msg: "对不起，文件下载失败，请重试！"}
	}
	return &model.ResJson{Code: 200, Msg: "ok", Data: buf}
}
